package ml;

import hr.irb.fastRandomForest.FastRandomForest;

import java.awt.Graphics2D;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.ArrayUtils;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYBarPainter;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.Day;
import org.jfree.data.time.MovingAverage;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.DefaultHighLowDataset;
import org.jfree.data.xy.IntervalXYDataset;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RefineryUtilities;
import org.jfree.util.ArrayUtilities;

import util.Candle;
import util.Util;
import weka.classifiers.AbstractClassifier;
import weka.core.Attribute;
import weka.core.DenseInstance;
import weka.core.Instances;

public class FRF {

	private ArrayList<Candle> prices;
	private AbstractClassifier classifier;
	private final int NUM_OF_TREES = 200;
	private final int SEED_NUM = 123;
	private final int MAX_DEPTH = 100;
	private Instances trainingData;
	private final int TRADING_PERIOD = 10;
	
	public FRF(String filename) {
	 createDataset(filename);
  }

 private void createDataset(String filename) {

	 try {
			BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filename),"UTF-8"));
			br.readLine();
			DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); 
			String line;

			ArrayList<Candle> candles = new ArrayList<Candle>();
			int numTrainingInstances= 0;
			while ((line = br.readLine()) != null) {
				StringTokenizer st = new StringTokenizer(line, ",");
				Date date = df.parse(st.nextToken());
				float priceOpen = Float.parseFloat(st.nextToken());
				float priceHigh = Float.parseFloat(st.nextToken());
				float priceLow = Float.parseFloat(st.nextToken());
				float priceClose = Float.parseFloat(st.nextToken());
				int volume = Integer.parseInt(st.nextToken());
				
				Candle candle = new Candle("", date, volume, priceHigh, priceLow, priceOpen, priceClose);
				candles.add(0, candle);
				
				if (date.before(df.parse("2010-01-01"))) numTrainingInstances++;
			}

			ArrayList<Attribute> attributes = new ArrayList<Attribute>();
			for (int i = 0; i < 10; i++) {
				attributes.add(new Attribute("price" + i));
			}
			ArrayList<String> classes = new ArrayList<String>(); 
			classes.add("class 0");
			classes.add("class 1");
			attributes.add(new Attribute("class", classes));
			trainingData = new Instances("train", attributes, numTrainingInstances);
			trainingData.setClassIndex(trainingData.numAttributes() - 1);
			int numGoodSamples = 0;
	    	
	        ArrayList<DenseInstance> goodIndices = new ArrayList<DenseInstance>(); 
	        ArrayList<DenseInstance> badIndices = new ArrayList<DenseInstance>(); 
			for (int i = 0; i < numTrainingInstances - 10; i++) {
	        	DenseInstance inst = new DenseInstance(attributes.size());
	        	float basePrice = 0f;
	        	float buyingPrice = 0f;
	        	for (int j = 0; j < trainingData.numAttributes(); j++) {
					Candle candle = candles.get(i+j);
	        		if (j == 0) {
	        			basePrice = candle.getPriceOpened();
	        			inst.setValue(j, 0);
	        		}
	        		else {
	        			inst.setValue(j, candle.getPriceClosed() - basePrice);
	        			buyingPrice = candle.getPriceClosed();
	        		}
	        	}
            	boolean isGoodTrade = true;
	        	for (int k = i + trainingData.numAttributes(); k < i + trainingData.numAttributes() + TRADING_PERIOD; k++) {
					Candle candle = candles.get(k);
					if (candle.getPriceLow() < buyingPrice) {
		            	isGoodTrade = false;
		            	break;
					}
	        	}
	        	if (isGoodTrade) {
	        		numGoodSamples++;
					System.out.println(numGoodSamples + "th good sample: " + i);
	        		goodIndices.add(inst);
	        	}
	        	else {
	        		badIndices.add(inst);
	        	}
			}
			
			Collections.shuffle(badIndices);
			
			for (int i = 0; i < goodIndices.size(); i++) {
				DenseInstance goodInst = goodIndices.get(i);
				goodInst.setDataset(trainingData);
				goodInst.setClassValue(0);
				trainingData.add(goodInst);
				DenseInstance badInst = badIndices.get(i);
				badInst.setDataset(trainingData);
				badInst.setClassValue(1);
				trainingData.add(badInst);
			}

			System.out.println("training size: " + trainingData.numInstances());
			FastRandomForest rf = new FastRandomForest();
			rf.setNumTrees(NUM_OF_TREES);
			rf.setNumFeatures(trainingData.numAttributes());
			rf.setSeed(SEED_NUM);
			rf.setMaxDepth(MAX_DEPTH);
			rf.setNumThreads(Runtime.getRuntime().availableProcessors());
			classifier = rf;
			classifier.buildClassifier(trainingData);

	    	Instances testData =  new Instances("test", attributes, candles.size() - trainingData.numInstances() - TRADING_PERIOD);
	    	testData.setClassIndex( attributes.size() - 1 );
			for (int i = trainingData.numInstances() - 11; i < candles.size() - 10 - TRADING_PERIOD; i++) {
	        	DenseInstance inst = new DenseInstance(attributes.size());
	        	float basePrice = 0f;
	        	float buyingPrice = 0f;
				for (int j = i; j < i + 10; j++) {
					Candle candle = candles.get(j);
	        		if (j == i) {
	        			basePrice = candle.getPriceOpened();
	        			inst.setValue(j-i, 0);
	        		}
	        		else {
	        			inst.setValue(j-i, candle.getPriceClosed() - basePrice);
	        			buyingPrice = candle.getPriceClosed();
	        		}
				}
            	inst.setDataset(testData);
            	inst.setClassValue(-1);
            	testData.add(inst);
			}
			int numGoodPredictions = 0;
			int numBadPredictions = 0;
			for (int i = numTrainingInstances - 1; i < candles.size() - TRADING_PERIOD; i++) {
				double result = classifier.classifyInstance(testData.instance(i-trainingData.numInstances()+1));
				if (result == 0f) {
					Candle candle = candles.get(i);
					System.out.println(i + ": " + candle.getDate());
					float buyingPrice = candle.getPriceClosed();
	            	boolean isGoodTrade = true;
					for (int j = i+1; j < i+11; j++) {
						Candle candle_ = candles.get(j);
						if (candle_.getPriceLow() < buyingPrice) {
			            	isGoodTrade = false;
			            	break;
						}
					}
					if (isGoodTrade) numGoodPredictions++;
					else numBadPredictions++;
					//System.out.println("is good trade: " + isGoodTrade);
				}
			}
			System.out.println("numGoodPredictions/numBadPredictions: " + numGoodPredictions+"/"+numBadPredictions);
	 }
	 catch (Exception e) {
		 e.printStackTrace();
	 }
  }

 public static void main(String args[]) {
	 FRF frf = new FRF(args[0]);
  }
}